#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <pthread.h>
#include <errno.h>
#include <error.h>

#include "fifo.h"
enum cache_nr {
	WEB_NR = 0,
	MIS_NR,
	CGI_NR,
	MAX_NR
};

struct env_data {
	struct fifo *pro_data;
	struct fifo *cache[MAX_NR];
	pthread_cond_t cond;
	pthread_mutex_t mutex;
	int quit;
};

struct env_data *env_data_init(int size, int cache_size, int lock_flag)
{
	struct env_data *thiz = NULL;
	int i;
	

	thiz = calloc(sizeof(struct env_data), 1);

	if (thiz != NULL) {
		thiz->pro_data = fifo_init(size, lock_flag);
		if (!thiz->pro_data) {
			fprintf(stderr, "create fifo error.");
			free(thiz);
			return NULL;
		}
		for (i=0; i< MAX_NR; i++) {
			thiz->cache[i] = fifo_init(cache_size, 0);
			if (!thiz->cache[i]) {
				fprintf(stderr, "create cache error.");
				while(i--) {
					fifo_free(thiz->cache[i]);
				}
				free(thiz);
			}
		}
		pthread_mutex_init(&thiz->mutex, NULL);
		pthread_cond_init(&thiz->cond, NULL);
		thiz->quit = 0;
	}

	return thiz;
}

int env_data_free(struct env_data *thiz)
{
	int i;
	if (thiz != NULL) {
		fifo_free(thiz->pro_data);
		for (i=0; i<MAX_NR; i++) {
			fifo_free(thiz->cache[i]);
		}
		pthread_mutex_destroy(&thiz->mutex);
		pthread_cond_destroy(&thiz->cond);
		free(thiz);
		return 0;
	}

	return -1;
}

void *web_parse(void *argv)
{	
	struct env_data *ped = (struct env_data *) argv;
	char buf[128]={0};
	int buf_len;
	int i=0;
	char *tmp;
	int tmp_len;
	struct timeval tv;

	while(!ped->quit) {
		gettimeofday(&tv, NULL);
		sprintf(buf, "%s=%d [time = %d] \n", __func__, i, (int)tv.tv_usec);
		buf_len=strlen(buf);

		if(pthread_mutex_trylock(&ped->mutex) == EBUSY) {
			fifo_write(ped->cache[WEB_NR], (char *)&buf_len, sizeof(int));
			fifo_write(ped->cache[WEB_NR], buf, strlen(buf));
		} else {
			tmp_len = fifo_len(ped->cache[WEB_NR]);
			if (tmp_len > 0) {
				tmp = calloc(sizeof(char)*tmp_len, 1);
				fifo_read(ped->cache[WEB_NR], tmp, tmp_len);
				fifo_write(ped->pro_data, tmp, tmp_len);
				free(tmp);
			}
			//pthread_mutex_lock(&ped->mutex);
			fifo_write(ped->pro_data, (char *)&buf_len, sizeof(int));
			fifo_write(ped->pro_data, buf, strlen(buf));
			pthread_cond_signal(&ped->cond);
			pthread_mutex_unlock(&ped->mutex);
		}
		sleep(1);
		i = (i++) % 9;
	}

	return (void *)(-1);
}
void *mis_parse(void *argv)
{	
	struct env_data *ped = (struct env_data *) argv;
	char buf[128]={0};
	int buf_len;
	int i=0;
	char *tmp;
	int tmp_len;
	struct timeval tv;

	while(!ped->quit) {
		gettimeofday(&tv, NULL);
		sprintf(buf, "%s=%d [time = %d] \n", __func__, i, (int)tv.tv_sec*1000+tv.tv_usec);
		buf_len=strlen(buf);

		if(pthread_mutex_trylock(&ped->mutex) == EBUSY) {
			fifo_write(ped->cache[MIS_NR], (char *)&buf_len, sizeof(int));
			fifo_write(ped->cache[MIS_NR], buf, strlen(buf));
		} else {
			tmp_len = fifo_len(ped->cache[MIS_NR]);
			if (tmp_len > 0) {
				tmp = calloc(sizeof(char)*tmp_len, 1);
				fifo_read(ped->cache[MIS_NR], tmp, tmp_len);
				fifo_write(ped->pro_data, tmp, tmp_len);
				free(tmp);
			}
			//pthread_mutex_lock(&ped->mutex);
			fifo_write(ped->pro_data, (char *)&buf_len, sizeof(int));
			fifo_write(ped->pro_data, buf, strlen(buf));
			pthread_cond_signal(&ped->cond);
			pthread_mutex_unlock(&ped->mutex);
		}
		sleep(1);
		i = (i++) % 9;
	}

	return (void *)(-1);
}
void *cgi_parse(void *argv)
{	
	struct env_data *ped = (struct env_data *) argv;
	char buf[128]={0};
	int buf_len;
	int i=0;
	char *tmp;
	int tmp_len;
	struct timeval tv;

	while(!ped->quit) {
		gettimeofday(&tv, NULL);
		sprintf(buf, "%s=%d [time = %d] \n", __func__, i, (int)tv.tv_sec);
		buf_len=strlen(buf);

		if(pthread_mutex_trylock(&ped->mutex) == EBUSY) {
			fifo_write(ped->cache[CGI_NR], (char *)&buf_len, sizeof(int));
			fifo_write(ped->cache[CGI_NR], buf, strlen(buf));
		} else {
			tmp_len = fifo_len(ped->cache[CGI_NR]);
			if (tmp_len > 0) {
				tmp = calloc(sizeof(char)*tmp_len, 1);
				fifo_read(ped->cache[CGI_NR], tmp, tmp_len);
				fifo_write(ped->pro_data, tmp, tmp_len);
				free(tmp);
			}
			//pthread_mutex_lock(&ped->mutex);
			fifo_write(ped->pro_data, (char *)&buf_len, sizeof(int));
			fifo_write(ped->pro_data, buf, strlen(buf));
			pthread_cond_signal(&ped->cond);
			pthread_mutex_unlock(&ped->mutex);
		}
		sleep(1);
		i = (i++) % 9;
	}

	return (void *)(-1);
}
int main(int argc, char *argv)
{
	struct env_data *ed = NULL;
	char buf[50]={0};
	int buf_len;

	pthread_t web_thread;
	pthread_t mis_thread;
	pthread_t cgi_thread;

	ed = env_data_init(1024, 256, 1);
	if (ed->pro_data == NULL) {
		printf("Create fifo error.\n");
		exit(1);
	}
	if (pthread_create(&web_thread, NULL, web_parse, (void*)ed) != 0) {
		perror("web_parse creat");
		env_data_free(ed);
		exit(1);
	}
	if (pthread_create(&web_thread, NULL, mis_parse, (void*)ed) != 0) {
		perror("web_parse creat");
		env_data_free(ed);
		exit(1);
	}
	if (pthread_create(&web_thread, NULL, cgi_parse, (void*)ed) != 0) {
		perror("web_parse creat");
		env_data_free(ed);
		exit(1);
	}

	while(!ed->quit) {

		pthread_mutex_lock(&ed->mutex);
		pthread_cond_wait(&ed->cond, &ed->mutex);
		while (fifo_len(ed->pro_data) > 0) {
			fifo_read(ed->pro_data, (char *)&buf_len, sizeof(int));
			fifo_read(ed->pro_data, buf, buf_len);
			/*maybe write to rs485*/
			fprintf(stderr, "read out data = %s", buf);
			memset(buf, 0, 50);
		}
		pthread_mutex_unlock(&ed->mutex);
	}

	pthread_join(web_thread, NULL);
	pthread_join(mis_thread, NULL);
	pthread_join(cgi_thread, NULL);

	env_data_free(ed);
	return 0;
}
